#ifndef LIMA_EXPORT_HPP
#define LIMA_EXPORT_HPP

#ifndef LIMA_EXPORT
#ifdef __cplusplus
#define LIMA_EXPORT extern "C"
#else
#define LIMA_EXPORT
#endif
#endif

#include <stdint.h>

typedef void *PLimaEngine;
typedef void *PLimaContext;
typedef void *PLimaVoid;

enum LimaEngineType{                  // engine类型
    LIMA,                       // 互动连麦
    VSTREAM,                    // 远程导播
};

enum LimaReason{
    OK,                         // 正常
    PEER_NOT_FOUND = 100,       // 发送消息时，用户找不到
    ROOM_RECONNECT,             // 信令通道重连，连续3次Reconnect后，会变成ROOM_CLOSED
    ROOM_CLOSED,                // 信令通道重连次数超过一定次数
    ROOM_REJECTED              // 被踢下线，不会自动重连，如需要重连，请再重新创建Engine
};

enum LimaSubscribeType{
VIDEO = 1 << 0,             // 订阅视频
AUDIO = 1 << 1,             // 订阅音频
STATE = 1 << 2,             // 订阅状态（上线和离线）
ALL = 0b111};                // 订阅所有


enum LimaVideoCodecType{
    LimaCodec_H264,
    LimaCodec_H265
};

enum LimaYUVFormat{
    LimaYUV_FormatYUV420Planar,     // i420 = YV21 
    LimaYUV_FormatYUV420SemiPlanar  // NV12 
};

enum VStreamLinkType{
    P2P,
    RELAY 
};

struct LimaVideoParameters {
    uint32_t width;
    uint32_t height;
    uint32_t fps;
    uint32_t minBitrateBps;     // 最小码率，单位为bps（如果为0，则为自适应码率）
    uint32_t maxBitrateBps;     // 最大码率，单位为bps
} ;

struct LimaAudioParameters {
    uint32_t bitsPerSample;
    uint32_t sampleRate;
    uint32_t channels;
    uint32_t frames;
} ;

struct LimaEngineParameters {
    // 是否自动播放收到的音频
    bool isAutoPlayAudio;
    // 是否自动采集音频
    bool isAutoRecordAudio;
    // 是否支持h265编码，默认是true
    bool isH265Supported;
} ;

// 订阅参数
struct LimaSubscribeUserParameters {
    /*
     * VideoCodec的编码优先级, 优先级列表指针，列表的个数和顺序与LimaVideoCodecType枚举值一致。优先级，数值越小，优先级越大
     *  一，默认优先级：
     *      H264 -> 1 
     *      H265 -> 2
     *  二，如果优先级为0则表示去掉该Codec，但不能所有编码都去掉
     *  三，在去掉某种编码的时候，需要注意对方编码的兼容性，比如：
     *      A支持h264，B支持h264，h265。此时如果B去掉h264，那么将出现A和B编码的兼容性问题
     */
    uint32_t videoCodecPriority[2];
} ;

/**
 * @brief Join频道回调接口
 * @param reason 是否成功，0：表示成功
 */
typedef void (*OnLimaJoinChannel)(PLimaEngine engine, int reason);

/**
 * @brief 新用户加入频道
 * @param uid 用户uid
 */
typedef void (*OnLimaUserJoined)(PLimaEngine engine, const char *uid);

/**
 * @brief 用户离开频道
 * @param uid 用户uid
 */
typedef void (*OnLimaUserClosed)(PLimaEngine engine, const char *uid, int reason);

/**
 * @brief 某个用户的yuv(I420)视频数据
 * @param uid 用户uid
 * @param y y数据
 * @param ySize y数据的大小
 * @param u u数据
 * @param uSize u数据的大小
 * @param v v数据
 * @param vSize v数据的大小
 * @param format yuv格式
 * @param width 宽度
 * @param height 高度
 * @param timestamp 时间戳
 * @remark  format为LimaYUV_FormatYUV420SemiPlanar时,y分量为yuv数据起始内存地址,strideY为当前frame的数据量(包括y,u,v分量),u,v为nullptr,strideU和strideV为0,formart为其他值时参考param说明 
 */
typedef void (*OnLimaVideoYUVFrame)(PLimaEngine engine, const char *uid, const char *y, uint32_t strideY, const char *u, uint32_t strideU, const char *v, uint32_t strideV, LimaYUVFormat format, uint32_t width, uint32_t height, int64_t timestamp);

/**
 * @brief 某个用户的pcm音频数据
 * @param uid 用户uid
 * @param pcm pcm数据 
 * @param bitsPerSample 采样点的位数
 * @param sampleRate 采样率
 * @param channels 频道数 
 * @param frames 采样点个数
 * @param timestamp 时间戳
 */
typedef void (*OnLimaAudioFrame)(PLimaEngine engine, const char *uid, const char *pcm, uint32_t bitsPerSample, uint32_t sampleRate, uint32_t channels, int64_t frames, int64_t timestamp);

/**
 * @brief 某个用户的消息
 * @param fromUid 发送该消息的用户uid
 * @param toUid 该消息的接受用户uid，如果为""，表示该消息是广播消息
 * @param message 消息数据
 * @param size 消息数据的大小
 */
typedef void (*OnLimaMessage)(PLimaEngine engine, const char *fromUid, const char *toUid, const char *message, uint32_t size);

/**
 * @brief 调用SendMsg函数后，消息确认回调 
 * @param messageId 消息Id，该值由SendMsg函数返回 
 * @param reason 是否成功，0：表示成功 
 */
typedef void (*OnLimaSendMsg)(PLimaEngine engine, uint32_t messageId, int reason);

/**
 * @brief 错误回调接口 
 * @param reason 错误原因 
 * @param message 错误消息字符串
 * @param isClosed 是否需要重新创建lima库 
 */
typedef void (*OnLimaError)(PLimaEngine engine, LimaReason reason);

/**
 * @brief 日志回调接口 
 * @param type 日志类型 
 * @param level 日志级别 
 * @param log 日志 
 */
typedef void (*OnLimaLog)(uint32_t type, const char *level, const char *log);

/**
 * @brief 日志控制是否上传接口 
 * @param douploadLog 是否需要上传 
 */
typedef void (*OnLimaLogUploadStateChanged)(PLimaEngine engine, bool douploadLog);

/**
 * @brief 状态回调接口，所有状态都会通过此接口进行回调
 * @param stats json格式
 * @param size stats字符串的长度
 * @remark lima的json格式如下：
 * {
	"local": {                              // 本地上传状态
		"rtt": 0,                           // rtt
		"score": 0,                         // 上传质量：0-10，0为最差，10为最好
		"state": "succeeded",               // 状态：succeeded为链接成功
		"video": {                          // video的上传状态
			"bytesSent": 0,                 // 总的发送字节数
			"framesEncoded": 0,             // 总的编码帧数
			"framesPerSecond": 0,           // 发送帧率
			"framesSent": 0                 // 发送帧数
		}
	},
	"remotes": [{                           // 远程接受状态
		"audio": {},                        // audio的状态
		"id": "0",                          // id
		"remote_score": 0,                  // 远程接受质量
		"rtt": 0,                           // rtt
		"score": 0,                         // 下载质量
		"state": "succeeded",               // 状态：succeeded为链接成功
		"video": {                          // video的接受状态
			"bytesReceived": 0,             // 总的接受字节数
			"framesDecoded": 0,             // 总的解码帧数
			"framesDropped": 0,             // 总的丢帧帧数
			"framesReceived": 0             // 总的接受帧数
		}
	}]
 *
 * @remark vstream的json格式如下：
     [{
        "uid": "0",                         // 对方uid
        "linktype":0,                       // 链接的类型，参考VStreamLinkType
        "localaddress":"",                  // 本地地址
        "remoteaddress":"",                 // 远程地址
        "video_receiver": {                 // video接受信息
            "fps": "0",                     // fps
            "width": "0",                   // width 
            "height": "0",                  // height
            "receivedBytes": "0"            // 接受字节数
            "codec": "video/H264"           // 编码类型mimeType
        },
        "video_sender": {                   // video发送信息
            "width": "0",                   // width
            "height": "0",                  // height
            "inputFPS": "0",                // 输入fps
            "sentFrames": "0"               // 已发送总的fps
            "sentBytes": "0",               // 已发送字节数
            "codec": "video/H264"           // 编码类型mimeType
        }
    }]
}
 */
typedef void (*OnLimaStats)(PLimaEngine engine, const char *stats, uint32_t size);

/**
 * @brief 初始化lima库，该函数在使用lima库之前调用一次即可
 */
LIMA_EXPORT void LimaInit();

/**
 * @brief 请在JNI_OnLoad之后传递Java_VM进行初始化
 * @param jvm Java_VM虚拟机指针
 * @remark Android平台特定接口
 */
LIMA_EXPORT void LimaAndroidInit(void *jvm);

/**
 * @brief 请在JNI_OnUnload进行销毁
 * @remark Android平台特定接口
 */
LIMA_EXPORT void LimaAndroidDestroy();

/**
 * @brief 销毁lima库，该函数在不在lima库后调用
 */
LIMA_EXPORT void LimaDestroy();

/**
 * @brief 创建lima实例 
 * @param url 连麦的地址，根据LimaEngineType不同而使用不同的地址：
 *  Lima：ws://lianmai.weizan.cn
 *  VStream：
 *      开发环境：gw-dev.vzan.com
 *      生产环境：gw.vzan.com
 * @param clientInfo 客户端自定义信息，格式为json
 * @remark clientInfo 建议包含以下字段：
 * {
 *  "app_name"   : "", // 产品名称，比如：芯像导播
 *  "app_version": "", // 程序版本，比如：1.0.0
 *  "device_info": {}, // 设备信息，比如：设备类型iphone7, cpu型号，内存大小等
 *  "system_info": {}, // 系统信息，比如：ios13
 * }
 * @param userData 附加数据
 * @param parameters 实例的设置，如果为nullptr，则使用默认设置
 * @return lima实例
 */
LIMA_EXPORT PLimaEngine LimaCreateEngine(const char *url, const char *clientInfo, LimaEngineType engineType, PLimaContext userContext, const LimaEngineParameters *parameters);

/**
 * @brief 获取engine的附加数据
 */
LIMA_EXPORT PLimaContext LimaGetUserContext(PLimaEngine engine);

/**
 * @brief 销毁lima实例。该销毁动作是同步的，会等待所有的回调执行完才返回
 * @param engine lima实例
 */
LIMA_EXPORT void LimaDestroyEngine(PLimaEngine engine);

/**
 * @brief 加入频道。在调用该函数前，请设置后相应的回调函数，不然可能错过某些回调通知事件
 * @param channelId 频道id
 * @param uid 用户uid
 * @remark  LIMA模式,一个频道的存续期间只join一次(断网的情况下,sdk会自动重联,不需要调用两次)
 *        VSTREAM模式,channelId没有意义,可以指定为空,仅仅是登陆服务器,一个engine存续期间只需要调用一次
 */
LIMA_EXPORT void LimaJoinChannel(PLimaEngine engine, const char *channelId, const char *uid);

/**
 * @brief 离开频道
 */
LIMA_EXPORT void LimaLeaveChannel(PLimaEngine engine); 

/**
 * @brief 发送视频数据，格式：I420
 * @param y y数据
 * @param ySize y的大小
 * @param u u数据
 * @param uSize u的大小
 * @param v v数据
 * @param vSize v的大小
 * @param timestamp 时间戳，单位：毫秒
 * @remark 调用前，请先调用LimaSetVideoParameters
 */
LIMA_EXPORT void LimaSendVideo(PLimaEngine engine, uint32_t width, uint32_t height, const char *y, uint32_t strideY, const char *u, uint32_t strideU, const char *v, uint32_t strideV, int64_t timestamp);

/**
 * @brief 发送音频数据
 * @param pcm pcm数据 
 * @param size pcm数据大小
 * @param timestamp 时间戳，单位：毫秒
 * @remark 调用前，请先调用LimaSetAudioParameters
 */
LIMA_EXPORT void LimaSendAudio(PLimaEngine engine, const char *pcm, uint32_t size, int64_t timestamp);

/**
 * @brief 开始录音
 */
LIMA_EXPORT void LimaStartRecording(PLimaEngine engine);

/**
 * @brief 停止录音
 */
LIMA_EXPORT void LimaStopRecording(PLimaEngine engine);

/**
 * @brief 设置视频格式
 * @param parameters 视频参数，详见LimaVideoParameters
 * @remark 请在LimaJoinChannel前进行调用
 */
LIMA_EXPORT void LimaSetVideoParameters(PLimaEngine engine, const LimaVideoParameters *parameters);

/**
 * @brief 设置音频格式
 * @param parameters 音频参数，详见LimaAudioParameters
 * @remark 请在LimaJoinChannel前进行调用
 */
LIMA_EXPORT void LimaSetAudioParameters(PLimaEngine engine, const LimaAudioParameters *parameters);

/**
 * @brief 发送消息
 * @param message 消息数据 
 * @param size 消息数据大小 
 * @param uid 准备发送的用户uid，如果该值为""，那么该消息将广播到整个频道
 * @return 消息确认Id，详见OnLimaSendMsg
 */
LIMA_EXPORT uint32_t LimaSendMsg(PLimaEngine engine, const char *message, uint32_t size, const char *uid);

/**
 * @brief 订阅某个用户的媒体流
 * @param uid 用户uid
 * @param parameters 订阅的参数
 */
LIMA_EXPORT void LimaSubscribeUser(PLimaEngine engine, const char *uid, LimaSubscribeType type, const LimaSubscribeUserParameters *parameters);

/**
 * @brief 取消订阅某个用户的媒体流
 * @param uid 用户uid
 */
LIMA_EXPORT void LimaUnSubscribeUser(PLimaEngine engine, const char *uid, LimaSubscribeType type);

/**
 * @brief 设置用户JoinChannel的回调函数
 */
LIMA_EXPORT void LimaSetOnJoinChannel(PLimaEngine engine, OnLimaJoinChannel onJoinChannel);

/**
 * @brief 设置新用户加入频道的回调函数
 */
LIMA_EXPORT void LimaSetOnUserJoined(PLimaEngine engine, OnLimaUserJoined onUserJoined);

/**
 * @brief 设置用户离开频道的回调函数
 */
LIMA_EXPORT void LimaSetOnUserClosed(PLimaEngine engine, OnLimaUserClosed onUserClosed);

/**
 * @brief 设置用户视频数据的回调函数
 * @remark 在iOS平台，使用硬解时，请参考：LimaSetOnVideoDecoderFrame
 */
LIMA_EXPORT void LimaSetOnVideoYUVFrame(PLimaEngine engine, OnLimaVideoYUVFrame onVideoFrame);

/**
 * @brief 设置用户音频数据的回调函数
 */
LIMA_EXPORT void LimaSetOnAudioFrame(PLimaEngine engine, OnLimaAudioFrame onAudioFrame);

/**
 * @brief 设置用户消息数据的回调函数
 */
LIMA_EXPORT void LimaSetOnMessage(PLimaEngine engine, OnLimaMessage onMessage);

/**
 * @brief 设置用户发送消息确认的回调函数
 */
LIMA_EXPORT void LimaSetOnSendMsg(PLimaEngine engine, OnLimaSendMsg OnSendMsg);

/**
 * @brief 设置lima库错误回调函数
 */
LIMA_EXPORT void LimaSetOnError(PLimaEngine engine, OnLimaError onError);

/**
 * @brief 设置状态回调接口，该接口每秒回调一次
 */
LIMA_EXPORT void LimaSetOnStats(PLimaEngine engine, OnLimaStats onStats);

/**
 * @brief 设置日志回调接口
 */
LIMA_EXPORT void LimaSetOnLog(OnLimaLog onLog);

/**
 * @brief 设置日志上传状态变化接口
 */
LIMA_EXPORT void LimaSetOnLogUploadStateChanged(PLimaEngine engine, OnLimaLogUploadStateChanged onLogUploadStateChanged);

/**
 * @brief 设置测试环境变量
 */
LIMA_EXPORT void LimaSetEnvValue(const char *key, const char *value);

/**
 **************************** 自定义解码器接口 ************************************
 */
/**
 * @brief 创建视频解码器回调接口
 * @param codecType 解码器的编码类型
 * @param id 解码器id
 * @return 该解码器的自定义上下文，以后所有回调都会传递该上下文
 */
typedef PLimaContext (*OnLimaVideoDecoderCreate)(PLimaEngine engine, LimaVideoCodecType codecType, uint32_t id);

/**
 * @brief 视频解码器准备解码回调
 * @param id 解码器id
 * @param packet 准备解码的数据
 * @param size packet的长度
 * @param timestamp packet的时间戳，该timestamp需要传给LimaVideoDecoderOnDecoded的timestamp参数
 */
typedef void (*OnLimaVideoDecoderPacket)(PLimaContext context, uint32_t id, const char *packet, uint32_t size, int64_t timestamp, uint32_t encodedwidth, uint32_t encodedheight);

/**
 * @brief 视频帧销毁通知接口
 * @param id 解码器id
 * @param frame 视频帧
 */
typedef void (*OnLimaVideoDecoderFrameDestroy)(PLimaContext context, uint32_t id, void *frame);

/**
 * @brief 视频解码器销毁通知接口
 * @param id 解码器id
 */
typedef void (*OnLimaVideoDecoderDestroy)(PLimaContext context, uint32_t id);

/**
 * @brief 视频帧器通知接口
 * @param uid 用户uid 
 * @param frame 视频帧
 * @remark 在ios平台，frame为CVImageBufferRef结构
 */
typedef void (*OnLimaVideoDecoderFrame)(PLimaEngine engine, const char *uid, void *frame);

/**
 * @brief 设置自定义解码器通知接口
 */
LIMA_EXPORT void LimaSetOnCreateVideoDecoder(PLimaEngine engine, OnLimaVideoDecoderCreate onCreate);

/**
 * @brief 设置解码器销毁通知接口
 */
LIMA_EXPORT void LimaSetOnVideoDecoderDestroy(PLimaEngine engine, OnLimaVideoDecoderDestroy onDestroy);

/**
 * @brief 设置解码器销毁数据包准备解码接口
 */
LIMA_EXPORT void LimaSetOnVideoDecoderPacket(PLimaEngine engine, OnLimaVideoDecoderPacket onPacket);

/**
 * @brief 设置视频帧通知接口
 */
LIMA_EXPORT void LimaSetOnVideoDecoderFrame(PLimaEngine engine, OnLimaVideoDecoderFrame onFrame);

/**
 * @brief 设置视频帧销毁接口
 */
LIMA_EXPORT void LimaSetOnVideoDecoderFrameDestroy(PLimaEngine engine, OnLimaVideoDecoderFrameDestroy onFrameDestroy);

/*
 * @brief 通知lima库视频解码完成
 * @param id 解码器id
 * @param frame 自定义视频帧，该frame将会在OnLimaVideoDecoderFrame通知
 * @param width 视频宽度
 * @param height 视频高度
 * @param timestamp packet的timestamp，该参数在OnLimaVideoDecoderPacket通知时携带 
 * @param decodeTimeMs 解码时间(可选)
 */
LIMA_EXPORT void LimaVideoDecoderOnDecoded(PLimaEngine engine, uint32_t id, void *frame, uint32_t width, uint32_t height, int64_t timestamp, int64_t decodeTimeMs);

/**
 **************************** 自定义编码器接口 ************************************
 */
/**
 * @brief 自定义编码器的初始化回调
 * @param codecType 编码器的类型
 * @param id id
 * @param width 宽度
 * @param height 高度
 * @param bitrate 比特率(bps)
 * @param fps 帧率
 */
typedef bool (*OnLimaVideoEncoderInit)(PLimaContext context, LimaVideoCodecType codecType, uint32_t id, uint32_t width, uint32_t height, uint32_t bitrate, uint32_t fps);

/**
 * @brief 自定义编码器的销毁回调
 */
typedef void (*OnLimaVideoEncoderDestroy)(PLimaContext context, uint32_t id);

/**
 * @brief 自定义编码器的编码信息改变
 * @param 当width指针不为nullptr，则表示width改变了
 * @param 当height指针不为nullptr，则表示height改变了
 * @param 当bitrate指针不为nullptr，则表示bitrate改变了
 * @param 当fps指针不为nullptr，则表示fps改变了
 */
typedef void (*OnLimaVideoEncoderParametersChanged)(PLimaContext context, uint32_t id, const uint32_t *width, const uint32_t *height, const uint32_t *bitrate, const uint32_t *fps);

/**
 * @brief 自定义编码器的编码回调
 * @param frame 自定义数据，请参考LimaSendVideoData的frame参数
 * @param timestamp 时间戳, 单位：毫秒
 */
typedef void (*OnLimaVideoEncoderFrameEncode)(PLimaContext context, uint32_t id, void *frame, int64_t timestamp, bool isForceKeyFrame);

/**
 * @brief 自定义编码器的数据销毁回调
 * @param frame 需要销毁的frame
 */
typedef void (*OnLimaVideoEncoderFrameDestroy)(PLimaContext context, void *frame);

/**
 * @brief 创建自定义编码器，如果使用该接口，请在LimaJoinChannel之前调用
 * @param context 自定义编码器的context
 */
LIMA_EXPORT void LimaCreateVideoEncoder(PLimaEngine engine, PLimaContext context);

/**
 * @brief 发送原始视频数据
 * @param frame 自定义数据
 * @param timestamp 时间戳，单位：毫秒
 * @remark 发送的frame需要是动态内存，并且只有在调用OnVideoEncoderFrameDestroy后，才能进行销毁
 */
LIMA_EXPORT void LimaVideoEncoderSendFrame(PLimaEngine engine, void *frame, int64_t timestamp);

/**
 * @brief 编码后数据的通知函数
 * @param id 传递OnVideoEncoderInit回调中的id
 * @param packet 编码后的数据
 * @param packetSize 编码后的数据大小
 * @param timestamp 时间戳，单位：毫秒
 * @param isKeyFrame 是否为关键帧
 */
LIMA_EXPORT void LimaVideoEncoderOnEncoded(PLimaEngine engine, uint32_t id, void *packet, uint32_t packetSize, int64_t timestamp, bool isKeyFrame);

/**
 * @brief 设置编码器的初始化回调函数
 */
LIMA_EXPORT void LimaSetOnVideoEncoderInit(PLimaEngine engine, OnLimaVideoEncoderInit onInit);

/**
 * @brief 设置编码器的销毁回调函数
 */
LIMA_EXPORT void LimaSetOnVideoEncoderDestroy(PLimaEngine engine, OnLimaVideoEncoderDestroy onDestroy);

/**
 * @brief 设置编码器的编码回调函数,通知编码
 */
LIMA_EXPORT void LimaSetOnVideoEncoderFrameEncode(PLimaEngine engine, OnLimaVideoEncoderFrameEncode onEncode);

/**
 * @brief 设置编码器的编码数据回调函数
 */
LIMA_EXPORT void LimaSetOnVideoEncoderFrameDestroy(PLimaEngine engine, OnLimaVideoEncoderFrameDestroy onFrameDestroy);

/**
 * @brief 设置编码器的编码改变回调函数
 */
LIMA_EXPORT void LimaSetOnVideoEncoderParametersChanged(PLimaEngine engine, OnLimaVideoEncoderParametersChanged onChanged);

#endif
